iT邦幫忙

2024 iThome 鐵人賽

DAY 23
0
Modern Web

一些讓你看來很強的 ORM - prisma系列 第 23

Day23. 一些讓你看來很強的 ORM - prisma ( Aggregate & Group by)

  • 分享至 

  • xImage
  •  

今天介紹一個很常會用到的實作 Aggregate ,可以幫助你整合你要的資訊,同時優化 DB query 的效能,這邊將會慢慢介紹,同時 prismaAggregateGroup by 兩種方法來做聚合的查詢,至於有什麼差別我們接著看下去~

在開始今天正文前,今天的內容會是基於Day21 的內容下去當作範例,modeldata都是基於這篇的內容 所以還沒看過的讀者記得先看一下~

Aggregate

Aggregate 用來讓你組合多筆資料成一個是 Int 或是 Float 的資料,例如你想查詢所有 user 的平均資料:

 const aggregations = await prismaClient.user.aggregate({
    _avg: {
      age: true
    }
 })

這邊就會根據你 query 的結果去平均所有 userage

{ _avg: { age: 50.12048192771084 } }

甚至進階一點你可以加上 whereorderBytake 然後再根據你 filter 的結果去平均 age:

  • Ordered by user (asc)
  • 透過 where 去找出 email 包含 gmail.comdata
  • 限制只拿取前10位 user

備註 : 使用 take 記得加上 order by 排序資料喔~

const aggregations = await prismaClient.user.aggregate({
    _avg: {
      age: true
    },
    where: {
      email: {
        contains: 'gmail.com'
      }
    },
    orderBy: {
      age: 'asc'
    },
    take: 10
})

這邊就是根據你塞選的條件最終得到的結果

{ _avg: { age: 11 } }

另外除了 _avg 你也可以用 _count 查看有多少 user 讓你去做平均

 const aggregations = await prismaClient.user.aggregate({
    _avg: {
      age: true
    },
    _count: {
      age: true
    }
  })
{ _avg: { age: 50.12048192771084 }, _count: { age: 83 } }

Aggregate Values Are Nullable

如果當你 aggregate 的欄位他是 nullable 最後 return 的內容他可能會是 0 或是 null,如果是0代表 query 不到資料,null 就是這個欄位沒有值

 const aggregations = await prismaClient.user.aggregate({
    _avg: {
      age: true
    },
    _count: {
      age: true
    }
  })

所以請大家看看 return 的結果

{
  _avg: {
    age: null
  },
  _count: {
    age: 9
  }
}

上面的內容{ _avg: { age: null } } 等同於以下的其中一種情境:

  • 沒有 user 資料
  • 所有 userage 欄位都是 null

所以這邊可以讓你區別 aggregatevalue 他沒有資料,或是全部 age 的欄位都是 null

GroupBy

別於 Aggregatereturn 單一一個 Int 或是 float 的欄位,GroupBy 在你 returnvalue 中可以讓你看到更多的欄位,以及優化你 query data 的內容,例如找到特定的 user 在特定的 city 他的平均 age 是多少

這邊簡單舉個 groupBy 的範例,以下是根據 country 去分組,然後根據分組的資料,平均整體的 profileViews

const groupUsers = await prismaClient.user.groupBy({
    by: ['country'],
    _sum: {
      profileViews: true
    }
  })

從這邊 returndata 可以看出在 Western Sahara 這個 city 總共 profileViews 有 324 個

[
//..
{ _sum: { profileViews: 324 }, country: 'Western Sahara' }
//..
]

然後 groupBy.by 可以不止 by 一個欄位他可以接受多個

const groupUsers = await prismaClient.user.groupBy({
    by: ['country', 'city'],
    _sum: {
      profileViews: true
    }
  })

這邊就是先根據 country 分組,然後再根據 city 去分類

[
//..
  {
    _sum: { profileViews: 324 },
    country: 'Western Sahara',
    city: 'Amelyhaven'
  }
//..
]

如果你只需要 by 一個欄位的話,可以不用 [] 去包

const groupUsers = await prisma.user.groupBy({
  by: 'country',
})

groupBy 支援 wherehaving 來幫你 filter data,這邊使用 where 先幫你在 group data 前先 filter data

 const groupUsers = await prismaClient.user.groupBy({
    by: ['country'],
    where: {
      email: {
        contains:'gmail.com'
      }
    },
    _sum: {
      profileViews: true
    }
  })
[
//..
{ _sum: { profileViews: 2399 }, country: 'Montserrat' }
//..
]

having 則是 filter 已經分組好的所有欄位,例如這邊我只想找到 profileViews 的總共數量是大於 4500 的資料

const groupUsers = await prismaClient.user.groupBy({
    by: ['country'],
    where: {
      email: {
        contains: 'gmail.com'
      }
    },
    _sum: {
      profileViews: true
    },
    having: {
      profileViews: {
        _avg: {
         gt: 4500,
        }
      }
    }
  })
[
  { _sum: { profileViews: 4595 }, country: 'Azerbaijan' },
  { _sum: { profileViews: 4665 }, country: 'Czechia' },
  { _sum: { profileViews: 4953 }, country: 'Gabon' },
  { _sum: { profileViews: 4943 }, country: 'Lebanon' }
] 

Where VS Having

簡單來說雖然 wherehaving 都是用來 filter data 但是彼此差別在於 filter 時機

  • wheregroupBy 前的塞選條件,用來減少 query data 的大小
  • having 則是根據 groupBy 後的結果做指標塞選

另外簡單舉個使用 WhereHaving 的錯誤用法,這邊主要先排除 country 不是 SwedenGhana 的資料,然後再分組

const fd = await prisma.user.groupBy({
  by: ['country'],
  where: {
    country: {
      notIn: ['Sweden', 'Ghana'],
    },
  },
  _sum: {
    profileViews: true,
  },
  having: {
    profileViews: {
      _min: {
        gte: 10,
      },
    },
  },
})

但以下的寫法跟上面的結果是一樣的,而且這邊的 filter 的結果效能還會筆上面全部用 where 的做法來得差,所以不推薦這樣做 having

const groupUsers = await prisma.user.groupBy({
  by: ['country'],
  where: {
    country: {
      not: 'Sweden',
    },
  },
  _sum: {
    profileViews: true,
  },
  having: {
    country: {
      not: 'Ghana',
    },
    profileViews: {
      _min: {
        gte: 10,
      },
    },
  },
})

Order By

Order By 也可以用在 groupBy :

  • 你可以 order 根據 by 的欄位
  • 可以 order aggregate 的結果例如 _sum
  • 使用 take 或是 skip 必須加上 order by

這邊就是根據以上的 demo 排序資料結果

const groupUsers = await prismaClient.user.groupBy({
    by: ['country'],
    where: {
      email: {
        contains: 'gmail.com'
      }
    },
    _sum: {
      profileViews: true
    },
    orderBy: {
      _sum: {
        profileViews: 'asc'
      }
    },
    having: {
      profileViews: {
        _avg: {
          gt: 4500
        }
      }
    }
  })

如此我們就可以排序 profileViews 的內容了

[
  { _sum: { profileViews: 4595 }, country: 'Azerbaijan' },
  { _sum: { profileViews: 4665 }, country: 'Czechia' },
  { _sum: { profileViews: 4943 }, country: 'Lebanon' },
  { _sum: { profileViews: 4953 }, country: 'Gabon' }
]

這邊使用 skip 排除第一筆資料,同時只拿根據 order by 的兩筆內容

const groupUsers = await prismaClient.user.groupBy({
    by: ['country'],
    where: {
      email: {
        contains: 'gmail.com'
      }
    },
    _sum: {
      profileViews: true
    },
    orderBy: {
      _sum: {
        profileViews: 'asc'
      }
    },
    having: {
      profileViews: {
        _avg: {
          gt: 4500
        }
      }
    },
    skip: 2,
    take: 1
  })
[ { _sum: { profileViews: 4943 }, country: 'Lebanon' } ]

常見問題

Q1 我可以在 groupBy() 中使用 select 嗎?

A1 你不能在 groupBy() 中使用 select。因為,by 中包含的所有欄位都會自動返回。

Q2 在 groupBy() 中使用 where 和 having 有什麼不同?

A2 where 在分組之前過濾所有記錄,而 having 過濾整個群組,並支援對聚合欄位值進行過濾,例如該組中特定欄位的平均值或總和。

Q3 groupBy() 和 Aggregate 有什麼不同?

aggregategroupBy() 都以一個或多個唯一欄位值將記錄分組。 groupBy() 允許你在每個群組內聚合資料,例如返回 post 上的平均瀏覽次數 , 而 aggregate 則不行,只能單純 return 例如 _sum 等等的 Int 或是 Float 的資料。

大家如果有問題可以來小弟的群組討論~

✅ 前端社群 :
https://lihi3.cc/kBe0Y


上一篇
Day22. 一些讓你看來很強的 ORM - prisma ( Logging & Debug)
下一篇
Day24. 一些讓你看來很強的 ORM - prisma (Count & Distinct)
系列文
一些讓你看來很強的 ORM - prisma30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言